<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body></body>
  <script>
    class MyPromise {
      state = "pending"; //状态 pending fulfilled rejected
      value = undefined; //成功的值
      reason = undefined; //失败的原因
      resolveCallbacks = []; //pending状态下，存储成功的回调函数
      rejectCallbacks = []; //pending状态下，存储失败的回调函数
      constructor(fn) {
        const resolveHandler = (value) => {
          if (this.state === "pending") {
            this.state = "fulfilled";
            this.value = value;
            this.resolveCallbacks.forEach((cb) => cb(this.value));
          }
        };
        const rejectHandler = (reason) => {
          if (this.state === "pending") {
            this.state = "rejected";
            this.reason = reason;
            this.rejectCallbacks.forEach((cb) => cb(this.reason));
          }
        };
        try {
          fn(resolveHandler, rejectHandler);
        } catch (err) {
          rejectHandler(err);
        }
      }
      then(fn1, fn2) {
        //当pending状态时，将成功的回调函数和失败的回调函数存储起来
        fn1 = typeof fn1 === "function" ? fn1 : (v) => v;
        fn2 = typeof fn2 === "function" ? fn2 : (e) => e;
        if (this.state === "pending") {
          const p1 = new MyPromise((resolve, reject) => {
            this.resolveCallbacks.push(() => {
              try {
                const newValue = fn1(this.value);
                resolve(newValue);
              } catch (err) {
                reject(err);
              }
            });
            this.rejectCallbacks.push(() => {
              try {
                const newReason = fn2(this.reason);
                reject(newReason);
              } catch (err) {
                reject(err);
              }
            });
          });
          return p1;
        }
        if (this.state === "fulfilled") {
          const p1 = new MyPromise((resolve, reject) => {
            try {
              const newValue = fn1(this.value);
              resolve(newValue);
            } catch (err) {
              reject(err);
            }
          });
          return p1;
        }
        if (this.state === "rejected") {
          const p1 = new MyPromise((resolve, reject) => {
            try {
              const newReason = fn2(this.reason);
              reject(newReason);
            } catch (err) {
              reject(err);
            }
          });
          return p1;
        }
      }
      catch(fn) {
        return this.then(null, fn);
      }
    }

    MyPromise.resolve = function (value) {
      return new MyPromise((resolve) => {
        resolve(value);
      });
    };
    MyPromise.reject = function (reason) {
      return new MyPromise((resolve, reject) => reject(reason));
    };
    MyPromise.all = function (promisesList = []) {
      return new MyPromise((resolve, reject) => {
        const result = []; //存放结果
        let length = promisesList.length;
        let count = 0; //计数器
        promisesList
          .forEach((p) => {
            p.then((value) => {
              result.push(value);
              count++;
              if (count === length) {
                resolve(result);
              }
            });
          })
          .catch((reason) => {
            reject(reason);
          });
      });
    };

    MyPromise.race = function (promisesList = []) {
      return new MyPromise((resolve, reject) => {
        let resolved = false;
        promisesList.forEach((p) => {
          p.then((value) => {
            if (!resolved) {
              resolve(value);
              resolved = true;
            }
          }).catch((err) => {
            reject(err);
          });
        });
      });
    };
    let p1 = MyPromise.resolve(1);
    let p2 = MyPromise.reject("错误信息");
    let result = MyPromise.race([p2, p1]).then((res) => {
      console.log(res, "res");
    });
    console.log(result, "result");
  </script>
</html>
